package com.xyoto.community.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xyoto.community.cache.CacheInstruction;
import com.xyoto.community.enums.CommonEnum;
import com.xyoto.community.exception.ExceptionUtils;
import com.xyoto.community.pojo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MultiValuedMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 内容检测工具类
 */
@Component
@Slf4j
public class ContentCheckUtils {

    @Autowired
    private RestTemplate restTemplate;
    private ObjectMapper objectMapper;

    @Autowired
    private RedisCommonUtils redisCommonUtils;

    public ContentCheckUtils() {
        objectMapper = new ObjectMapper();
        // 忽略反序列化时未知的字段
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @Autowired
    private AccessTokenFactory accessTokenFactory;

    // 标签值，100 正常；10001 广告；20001 时政；20002 色情；20003 辱骂；20006 违法犯罪；20008 欺诈；20012 低俗；20013 版权；21000 其他
    private static Map<Integer, String> labelMap;

    static {
        labelMap = new HashMap<>();
        labelMap.put(10001, "广告");
        labelMap.put(20001, "时政");
        labelMap.put(20002, "色情");
        labelMap.put(20003, "辱骂");
        labelMap.put(20006, "违法犯罪");
        labelMap.put(20008, "欺诈");
        labelMap.put(20012, "低俗");
        labelMap.put(20013, "版权");
        labelMap.put(21000, "其他");
    }

    // 重试次数
    int RETRY_TIMES = 3;

    // 重试次数计数器
    int retryTimesCount = 0;

    /**
     * 文本检测
     *
     * @param content 需要检测的文本
     * @return ContentCheckResult，若没有错误，则返回值为null，有错误则包括错误代码和错误信息
     */
    public ContentCheckResult checkContent(String content) {
        // 封装参数
        ContentCheckRequestParam requestParam = new ContentCheckRequestParam();
        requestParam.setContent(content);
        // 文本检测
        log.info("ContentCheckRequestParam = {}", requestParam);
        ContentCheckResponse checkResponse = check(requestParam);
        // 封装结果
        ContentCheckResult contentCheckResult = null;
        ContentCheckResponseResult result = checkResponse.getResult();
        if (result == null && retryTimesCount < RETRY_TIMES){
            retryTimesCount++;
            // result为空，清空accessToken重试
            redisCommonUtils.del(CacheInstruction.ACCESS_TOKEN);
            return checkContent(content);
        }else if (result == null){
            // 重试3次后仍然检查失败
            return new ContentCheckResult(44004,"文本检查失败");
        }
        Integer label = result.getLabel();
        if (label != 100) {
            // 获取一个不通过的原因
            List<ContentCheckResponseDetail> detailList = checkResponse.getDetail();
            for (ContentCheckResponseDetail detail : detailList) {
                Integer label1 = detail.getLabel();
                if (label1 != null && label1 != 100) {
                    contentCheckResult = new ContentCheckResult(label1, labelMap.get(label1));
                }
            }
        }
        // 返回结果
        return contentCheckResult;
    }

    /**
     * 调用微信api进行文本检测
     *
     * @param requestParam
     * @return
     */
    private ContentCheckResponse check(ContentCheckRequestParam requestParam) {

        // 获取accessToken
        String accessToken = accessTokenFactory.getAccessToken();

        // 微信内容检测api链接
        String url = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=" + accessToken;

        // 发起请求
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestParam, String.class);
        log.info("鉴定结果json = {}", responseEntity);
        int statusCodeValue = responseEntity.getStatusCodeValue();
        if (statusCodeValue != 200) {
            // 请求微信接口失败
            ExceptionUtils.error(CommonEnum.ERROR);
        }
        // 解析json
        String json = responseEntity.getBody();
        ContentCheckResponse response = null;
        try {
            response = objectMapper.readValue(json, ContentCheckResponse.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return response;
    }

    /**
     * 图片内容检测，判断是否安全，只能检测单张
     *
     * @param multipartFile 需要进行检测的图片
     * @return 若信息不安全则返回值为ContentCheckResult对象，包含了错误信息，如安全则返回值为null
     */
    public ContentCheckResult checkImg(MultipartFile multipartFile) {
        // 封装请求
        HttpEntity httpEntity = packageRequestParam(multipartFile);
        // 调用api
        ContentCheckResult contentCheckResult = checkImg(httpEntity);
        if (contentCheckResult != null && contentCheckResult.getErrcode() != 87014) {
            // 图片正常
            contentCheckResult = null;
        }
        // 返回结果
        return contentCheckResult;
    }

    /**
     * 封装图片请求参数
     *
     * @param multipartFile
     * @return
     */
    private HttpEntity packageRequestParam(MultipartFile multipartFile) {
        // 设置请求头为提交一个文件表单
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        // 将文件放入Multi
        MultiValueMap<String, Object> paramsMap = new LinkedMultiValueMap<>();
        try {
            ByteArrayResource byteArrayResource = new ByteArrayResource(multipartFile.getBytes()){
                @Override
                public String getFilename() {
                    // 写入流时，附带上文件名
                    return multipartFile.getOriginalFilename();
                }
            };
            paramsMap.add("media", byteArrayResource);
        } catch (IOException e) {
            log.error("【封装图片请求参数】，multiPartFile = {}", multipartFile);
            e.printStackTrace();
        }
        return new HttpEntity<>(paramsMap, headers);
    }

    /**
     * 调用微信api进行文本检测
     *
     * @param httpEntity
     * @return
     */
    private ContentCheckResult checkImg(HttpEntity httpEntity) {
        // 获取accessToken
        String accessToken = accessTokenFactory.getAccessToken();
        String url = "https://api.weixin.qq.com/wxa/img_sec_check?access_token=" + accessToken;
        // 调用微信api
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class);
        if (responseEntity.getStatusCodeValue() != 200) {
            log.error("【调用微信api进行图片检测】，调用微信接口失败，请求参数为：{}，accessToken为{}", httpEntity, accessToken);
            ExceptionUtils.error(CommonEnum.ERROR);
        }
        // 解析结果
        String json = responseEntity.getBody();
        ContentCheckResult contentCheckResult = null;
        try {
            contentCheckResult = objectMapper.readValue(json, ContentCheckResult.class);
        } catch (JsonProcessingException ignored) {
            log.error("【调用微信api进行文本检测】反序列化错误，请求参数为{}，响应结果为：{}", httpEntity, json);
        }
        return contentCheckResult;
    }

}
